Rules for Tools/ToolMaster Structure
The ToolMaster Structure
Since multiple copies of a Tool can be made, you don't define each Tool individually in
your program code. Instead, a central data structure, ToolMaster, provides all of the
information needed to define the Tool. Multiple copies of a Tool all refer to the
ToolMaster structure for the information they share, such as name, icon image, the size
of the Tool structure itself, and the routine that processes events when they enter a
Tool. When Bars&Pipes first loads a Tool from a file, it calls a routine in the Tool
file that initializes the ToolMaster structure and returns a pointer to it. It is the
ToolMaster structure that is represented in the ToolBox. When you drag a Tool from the
ToolBox onto the PipeLine, Bars&Pipes consults the ToolMaster structure to determine
everything it needs to know in order to create the instance of the Tool. Each Tool, in
turn, carries a pointer to the ToolMaster structure that describes it.
struct ToolMaster {
struct ToolMaster *next; /* Next in this list. */
long toolid; /* Tool ID. */
struct Image *image; /* Icon for this tool. */
struct Image *upimage; /* Icon for branching up. */
short x,y; /* Position in toolbox. */
char name[100]; /* Tool name. */
char filename[100]; /* File location. */
struct Tool *(*createtool)(); /* Allocate a new tool. */
void (*edittool)(); /* Edit tool parameters. */
struct Event *(*processevent)();/* Process an event. */
void (*processclip)(); /* Unused. */
void (*deletetool)(); /* Routine to delete. */
void (*removetool)(); /* Routine to close. */
long (*savesize)(); /* Returns size for save. */
long (*savetool)(); /* Routine to save. */
struct Tool *(*loadtool)(); /* Routine to load. */
long (*expanda)(); /* Future routine? */
long (*expandb)(); /* Future routine? */
long (*expandc)(); /* Future routine? */
long segment; /* Segment list. */
long altsegment; /* Alternate segment. */
struct Track *intrack; /* Input track. */
short toolsize; /* Tool size. */
char inedit; /* Editing now. */
char selected; /* Icon selected flag. */
long tooltype; /* Type of tool. */
};
Fields in the ToolMaster structure that are of interest:
`toolid'
Each ToolMaster has a unique long word identifier, composed of
four ASCII characters clumped together. For example, the MIDI Out
Tool is "MIOT", or 0x4D494F54. It is very important that no two
commercially available Tools have the same identifier; please
communicate with The Blue Ribbon Soundworks to make sure your Tool
identifiers are unique.
`tooltype'
A set of bits determine the characteristics of each Tool (see
Tool Type Flags). These bits are placed in the tooltype field.
Some of the bits are:
`TOOL_INPUT'
This is an input Tool. It can only exist on the input of a
PipeLine as the source for a Track.
`TOOL_OUTPUT'
This is an output Tool. It can only exist as the very last
Tool on a Track's PipeLine.
`TOOL_NORMAL'
This Tool can be placed in any position on a PipeLine, other
than input or output.
`TOOL_ONTIME'
Notes sent to this Tool must be given to the Tool by
Bars&Pipes' pipeline system exactly at the time stamped on
the note. If not, the note may arrive early, providing extra
time for processing overhead. Most Tools do not need to set
this flag. The MIDI Out Tool does, because it needs to send
notes to synthesizers when they are due to be played, not
ahead of time.
`TOOL_BRANCHOUT'
This Tool can be connected by a vertical pipe to a Merge In
Tool on another PipeLine.
`image' `upimage'
You must provide a pointer to an Intuition Image structure that
defines the Tool's icon in the image field, and this Image must
have the dimensions of 24 pixels wide by 12 pixels high by 3
bitplanes deep (8 colors.) If the Tool is capable of branching,
place an alternate Image that shows how the Tool would look if it
were branching up (connecting to a pipe from above instead of
below) in the `upimage' field. We use DPaint to create icons and
a public domain utility, icon2c, to translate them into C source
code. Several sample icon images are included in the source disk.
Load one into DPaint (as a brush) and use it to set the color
palette. Paint your own icon and save it as a brush. The brush
must be exactly 24 pixels wide by 12 pixels high. Convert the
brush to C code with icon2c, then insert the code in your source
file with your text editor. Be sure to compile the icon to load
into chip memory. (This is easily accomplished in Lattice by using
the chip keyword.)
`toolsize'
In order to create, delete, save, and load copies of your Tool,
Bars&Pipes needs to know the size of the Tool structure for your
Tool. This size can vary from Tool to Tool since you can add
additional fields to the Tool structure for your Tool. (For an
example, look at the DelayTool structure in the Delay Tool example
code). You can, in most cases, just use the `sizeof()' function
of the C compiler to calculate the size of the `Tool' structure.
Place the result in the toolsize field.
`name'
Each Tool has a name, which is a null terminated ASCII string, up
to 100 characters in length.
`processevent'
Probably the single most important feature of a Tool, processevent
points to a routine that handles each MIDI Event sent to this
Tool. This routine defines the behavior of the Tool. Although
the processevent routine is always passed one Event, it may return
any number of Events, strung in a linked list. For example, the
Echo Tool returns all of the echo notes it generates linked in a
list. On the other hand, the Plug Tool frees each note it
receives and returns 0.
`edittool'
Many Tools require user interfaces that can be opened by double
clicking on the Tool in the PipeLine. Define a reentrant routine
that opens a window and lets the user set the Tool's parameters
and place a pointer to that routine in edittool. For those of you
who might be a bit foggy on writing reentrant code, just remember
that you shouldn't use global or static variables in your code.
If you do, you'll have to make sure that you provide adequate
locking mechanisms for those variables to ensure that if several
copies of your Tool are executing at the same time, they won't
trash your global or static variables. As long as you store your
data in the Tool structure, writing a reentrant routine should
pose no problem - with one notable exception. Intuition
structures (such as windows for your Tool) need to be duplicated
for each Tool. Inovatools provides a function to duplicate a
Window structure and all of the Gadgets associated with it. If
you aren't using Inovatools, you'll have to write your own routine
to duplicate the Window structure.
`createtool'
This is a routine that Bars&Pipes calls to allocate a Tool
structure. Usually, you don't need to provide this routine,
because Bars&Pipes knows how large a Tool structure to create just
by checking the toolsize field. If, however, you need to do
anything special like allocate dynamic data structures or
initialize tasks or other resources, write a routine to do so and
have `createtool' point to it. Then, Bars&Pipes calls your
routine to allocate and initialize the Tool structure rather than
doing so itself. Your `createtool' routine must return a pointer
to the allocated Tool, or 0 if it is unable to do so.
`deletetool'
This is the companion to `createtool'. Once again, Bars&Pipes is
happy to delete your Tool since it knows the size of the Tools
structure. But, if you have done anything special, like allocate
dynamic data structures, provide a deletetool routine to remove
them along with the Tool. `deletetool' is passed one parameter:
the Tool to delete.
`removetool'
If you provide a `removetool' routine, Bars&Pipes will call it
once when Bars&Pipes removes your ToolMaster from the system. This
occurs either when Bars&Pipes is closing down or when the user
selects Remove Tool from the ToolBox menu. Most Tools don't have
a `removetool' routine. Others, like the MIDI In Tool, use the
`removetool' routine to deallocate system resources previously
allocated when the Tool was loaded. Tools that install transport
handlers usually have a removetool routine to remove the handler.
`loadtool'
Usually, when Bars&Pipes reads a Tool from disk as part of a song,
it reads a chunk of data equivalent to the size of the Tool
structure (as defined in `toolsize'), creates a Tool structure, and
places the data in it. If, however, the Tool provides a `loadtool'
routine, that routine reads the data directly. This is necessary
if the Tool has dynamic data structures. Tools that use
`loadtool' also have `savetool' and `sizetool' routines for saving
their data. Bars&Pipes calls `loadtool' with two parameters: a
file pointer and the size of the data segment to read.
long file; long size; struct Tool *tool;
tool = loadtool(file,size);
The `loadtool' routine returns either a pointer to a Tool
structure or 0 for an unsuccessful read. In either case, it must
read exactly the number of bytes in the data segment. For reading
and writing to disk, Bars&Pipes uses its own buffered file i/o
system. As a result, the file pointer must be used in conjunction
with the `fastread()' (see fastopen()) call (See the Bars&Pipes
library summary.)
`savesize'
Provide this routine in conjunction with `loadtool' and `savetool'
only if your Tool saves a variable amount of data to disk as part
of a song. When Bars&Pipes saves a song, it needs to know how
large each data section is before saving it. It will call your
`savesize' routine, passing one parameter - the Tool to save. Your
`savesize' routine should calculate the number of bytes needed to
save the tool and return that number.
`savetool'
The second step in saving a Tool involves a second routine that
you provide - `savetool'. Bars&Pipe calls `savetool', passing it
the file handle and the Tool to save. You must use the
`fastwrite()' command (see fastopen()) for writing. (See the
Bars&Pipes library summary.) Your `savetool' routine first writes
the four letter tool identifier, then the size of the Tool data,
then the data itself.
savetool(file,tool) long file; struct Tool *tool; {
long size;
(*function->fastwrite)(file,&tool->toolmaster->toolid,4);
size = savesize(tool);
(*functions->fastwrite)(file,&size,4);
/*Save the tool structure, etc., here. */
/* If unable to complete the save, return 1, */
/* otherwise return 0 for success. */
return(0); }
We've talked about the ToolMaster structure, and in doing so, we've also talked
about the Tool structure which accompanies each instance of a ToolMaster structure.
Let's look at the Tool structure in more detail.